#if defined(__linux__)

#include "Epoll.h"
#include "../reactor/Reactor.h"

using namespace std;
using namespace network;

Epoll::Epoll(int max_size) : max_size_(max_size) {
    epoll_fd_ = epoll_create(1);
    events_ = shared_ptr<struct epoll_event>(new struct epoll_event[max_size_],
                                             [](struct epoll_event *evs) { delete[] evs; });
}

struct epoll_event &Epoll::operator[](int index) {
    if (index > max_size_) {
        throw runtime_error("out of max epoll_event array length");
    }
    return events_.get()[index];
}

bool Epoll::add(int fd, EventType event_type) {
    uint32_t events = EPOLLET;
    if (hasEvent(event_type, EventType::Read)) {
        events |= EPOLLIN;
    }
    if (hasEvent(event_type, EventType::Write)) {
        events |= EPOLLOUT;
    }
    if (hasEvent(event_type, EventType::Close)) {
        events |= EPOLLIN;
    }
    event_.events = events;
    event_.data.fd = fd;
    int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event_);
    return ret == 0;
}

bool Epoll::mod(int fd, EventType event) {
    uint32_t events = EPOLLET;
    if (hasEvent(event, EventType::Read) || hasEvent(event, EventType::Close)) {
        events |= EPOLLIN;
    }
    if (hasEvent(event, EventType::Write)) {
        events |= EPOLLOUT;
    }
    event_.events = events;
    event_.data.fd = fd;
    int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &event_);
    return ret == 0;
}

bool Epoll::del(int fd) {
    int ret = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr);
    return ret == 0;
}

int Epoll::dispatch(Reactor *reactor) {
    int ret = epoll_wait(epoll_fd_, events_.get(), max_size_, 0);
    if (ret < 0) {
        throw runtime_error("epoll_wait return < 0");
    }
    for (int i = 0; i < ret; ++i) {
        int fd = events_.get()[i].data.fd;
        if (events_.get()[i].events & EPOLLIN) {
            reactor->addEvent(fd, EventType::Read);
        } else if (events_.get()[i].events & EPOLLOUT) {
            reactor->addEvent(fd, EventType::Write);
        } else if (events_.get()[i].events & EPOLLRDHUP) {
            reactor->addEvent(fd, EventType::Close);
        }
    }
    return ret;
}

#endif